package fr.vince.montecarlo;

import java.util.Random;

import javax.swing.JProgressBar;

/**
 * Monte carlo representation for simple thread computation
 * @author Vince
 *
 */
public class MonteCarlo {
	private Action action;
	private Double actualPrice; // S
	private Double strikePrice; // X
	private Double time;// T
	private Double interest;// r
	private Double cost;// b
	private Double volatility;// v
	private Integer steps;// nSteps
	private Integer simulations;// nSimulation

	/**
	 * default constructor
	 * @param action
	 * @param actualPrice
	 * @param strikePrice
	 * @param time
	 * @param interest
	 * @param cost
	 * @param volatility
	 * @param steps
	 * @param simulations
	 */
	public MonteCarlo(Action action, Double actualPrice, Double strikePrice,
			Double time, Double interest, Double cost, Double volatility,
			Integer steps, Integer simulations) {
		super();
		this.action = action;
		this.actualPrice = actualPrice;
		this.strikePrice = strikePrice;
		this.time = time;
		this.interest = interest;
		this.cost = cost;
		this.volatility = volatility;
		this.steps = steps;
		this.simulations = simulations;
	}

	public Double doMonteCarlo(Action action, double actualPrice,
			double strikePrice, double time, double interest, double cost,
			double volatility, int steps, int simulations) {
		long start = System.nanoTime();
		System.out.println("actual price : " + actualPrice + "  strikePrice : "
				+ strikePrice + "   time : " + time + "    interest : "
				+ interest + "    cost: " + cost + "    volalitility :"
				+ volatility + "     steps: " + steps + "     simulations : "
				+ simulations);
		int z = 0;
		double dt;
		double st;
		double sum = 0;
		double drift;
		double sqrt;

		dt = time / steps;
		drift = (cost - Math.pow(volatility, 2) / 2) * dt;
		sqrt = volatility * Math.sqrt(dt);
		switch (action) {
		case CALL:
			z = 1;
			break;
		case PUT:
			z = -1;
			break;
		}
		for (int i = 0; i < simulations; i++) {
			st = actualPrice;
			for (int j = 0; j < steps; j++) {
				Random rand = new Random();
				st *= Math.exp(drift + sqrt * rand.nextGaussian());
				// System.out.println("debug 1 : "+st);
				// System.out.println("i*j : "+(i*steps)+j+"    debug : "+(double)((i*steps+j)/(simulations*steps))+"%");
			}
			sum += Math.max(z * (st - strikePrice), 0);
		}
		System.out.println("Execution time "
				+ ((System.nanoTime() - start) / 1000000000.0) + " s");
		return Math.exp(-interest * time) * (sum / simulations);
	}

	/**
	 * compute the monte carlo algorithm
	 * @param pb the progress bar on the interface
	 * @return the result
	 */
	public Double doMonteCarlo(JProgressBar pb) {
		long start = System.nanoTime();
		System.out.println("actual price : " + actualPrice + "  strikePrice : "
				+ strikePrice + "   time : " + time + "    interest : "
				+ interest + "    cost: " + cost + "    volalitility :"
				+ volatility + "     steps: " + steps + "     simulations : "
				+ simulations);
		int z = 0;
		double dt;
		double st;
		double sum = 0;
		double drift;
		double sqrt;

		dt = time / steps;
		drift = (cost - Math.pow(volatility, 2) / 2) * dt;
		sqrt = volatility * Math.sqrt(dt);
		switch (action) {
		case CALL:
			z = 1;
			break;
		case PUT:
			z = -1;
			break;
		}
		pb.setMaximum(simulations*steps);
		for (int i = 0; i < simulations; i++) {
			st = actualPrice;
			for (int j = 0; j < steps; j++) {
				Random rand = new Random();
				st *= Math.exp(drift + sqrt * rand.nextGaussian());
				pb.setValue(j+i*steps);
				// System.out.println("debug 1 : "+st);
				// System.out.println("i*j : "+(i*steps)+j+"    debug : "+(double)((i*steps+j)/(simulations*steps))+"%");
			}
			sum += Math.max(z * (st - strikePrice), 0);
		}
		System.out.println("Execution time "
				+ ((System.nanoTime() - start) / 1000000000.0) + " s");
		return Math.exp(-interest * time) * (sum / simulations);
	}

}
